using System;
using System.IO;

namespace IgorPavlov.SevenZip
{
    public static class LZMACompression
    {
        public static void Compress(MemoryStream inMS, MemoryStream outMS)
        {
            Encode(inMS, outMS);
        }

        public static void Deflate(MemoryStream inMS, MemoryStream outMS)
        {
            Decode(inMS, outMS);
        }

        private static void Encode(Stream inStream, Stream outStream)
        {
            const string mf = "bt4";
            const Int32 dictionary = 1 << 23;

            const Int32 posStateBits = 2;
            const Int32 litContextBits = 3;
            const Int32 litPosBits = 0;
            const Int32 algorithm = 2;
            const Int32 numFastBytes = 128;

            const bool eos = false;

            Core.CoderPropID[] propIDs = 
				{
                    Core.CoderPropID.DictionarySize,
                    Core.CoderPropID.PosStateBits,
                    Core.CoderPropID.LitContextBits,
					Core.CoderPropID.LitPosBits,
                    Core.CoderPropID.Algorithm,
                    Core.CoderPropID.NumFastBytes,
                    Core.CoderPropID.MatchFinder,
                    Core.CoderPropID.EndMarker
				};

            object[] properties = 
				{
					dictionary,
					posStateBits,
					litContextBits,
					litPosBits,
					algorithm,
					numFastBytes,
					mf,
					eos
				};

            Core.Compression.LZMA.Encoder encoder = new Core.Compression.LZMA.Encoder();
            encoder.SetCoderProperties(propIDs, properties);
            encoder.WriteCoderProperties(outStream);

            long fileSize = inStream.Length;

            for (int i = 0; i < 8; i++)
                outStream.WriteByte((Byte)(fileSize >> (8 * i)));

            encoder.Code(inStream, outStream, -1, -1, null);
        }

        private static void Decode(Stream inStream, Stream outStream)
        {
            byte[] properties = new byte[5];

            if (inStream.Read(properties, 0, 5) != 5)
                throw (new Exception("input .lzma is too short"));

            Core.Compression.LZMA.Decoder decoder = new Core.Compression.LZMA.Decoder();
            decoder.SetDecoderProperties(properties);

            long outSize = 0;
            for (int i = 0; i < 8; i++)
            {
                int v = inStream.ReadByte();
                if (v < 0)
                    throw (new Exception("Can't Read 1"));
                outSize |= ((long)(byte)v) << (8 * i);
            }

            long compressedSize = inStream.Length - inStream.Position;

            decoder.Code(inStream, outStream, compressedSize, outSize, null);
        }

    }
}
